package org.example.content;

import cn.hutool.core.util.StrUtil;
import org.example.App;
import org.example.annotation.*;
import org.example.service.UserServiceImpl;
import org.example.spring.BeanNameAware;
import org.example.spring.BeanPostProcessor;
import org.example.spring.InitializingBean;
import org.example.utils.SysUtils;

import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @description：
 * @author：dxs
 * @date：2024/4/2 09:19
 */
public class ApplicationContent {
    private Class<?> applicationClass;
    private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMaps = new ConcurrentHashMap<>();
    private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();
    private List<BeanPostProcessor> beanPostProcessors = new ArrayList<>();

    public ApplicationContent(Class<?> applicationClass) {
        this.applicationClass = applicationClass;
        // 解析配置类-》扫描包-》解析注解-》初始化beanDefinition
        scan(applicationClass);

        Set<Map.Entry<String, BeanDefinition>> entries = beanDefinitionMaps.entrySet();
        for (Map.Entry<String, BeanDefinition> entry : entries) {
            BeanDefinition beanDefinition = entry.getValue();
            String beanName = entry.getKey();
            if (beanDefinition.getScope().equals("singleton")) {
                Object bean = createBean(beanName,beanDefinition);
                // 添加到单例池
                singletonObjects.put(beanName, bean);
            }
        }
    }

    private Object createBean(String beanName,BeanDefinition beanDefinition) {
        // 创建bean
        Object bean = null;
        Class beanClass= beanDefinition.getBeanClass();
        try {
            bean = beanClass.newInstance();
            // 依赖注入
            Field[] declaredFields = beanClass.getDeclaredFields();
            for (Field declaredField : declaredFields) {
                if(declaredField.isAnnotationPresent(Autowired.class)){
                    String name = declaredField.getName();
                    declaredField.setAccessible(true);
                    declaredField.set(bean,getBean(name));
                }
            }
            // beanNameAware 设置
            if(bean instanceof BeanNameAware){
                ((BeanNameAware) bean).setBeanName(beanName);
            }

            //beanPostProcessor postProcessBeforeInitialization
            for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
                bean = beanPostProcessor.postProcessBeforeInitialization(bean, beanName);
            }
            // 回调InitializingBean初始化方法;更改或者校验字段
            if(bean instanceof InitializingBean){
                try {
                    ((InitializingBean) bean).afterPropertiesSet();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
            // beanPostProcessor postProcessAfterInitialization
            for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
                bean = beanPostProcessor.postProcessAfterInitialization(bean, beanName);
            }


        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }

        return bean;


    }
    // 根据配置类扫描包，完成beanDefinition的注册
    private void scan(Class applicationClass) {
        // 判断是否有ComponentScan注解
        if (!applicationClass.isAnnotationPresent(ComponentScan.class)) {
            throw new RuntimeException("init fail");
        }
        // 获取包名
        ComponentScan componentScan = (ComponentScan) applicationClass.getDeclaredAnnotation(ComponentScan.class);
        String packageName = componentScan.value();
        packageName = packageName.replace(".", "/");
        ClassLoader classLoader = componentScan.getClass().getClassLoader();
        // 获取包路径
        URL resource = classLoader.getResource(packageName);
        String fileAddress = resource.getFile();
        File file = new File(fileAddress);
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File file1 : files) {
                initBeans(file1, packageName, classLoader);
            }
        }

    }
    // 初始化beanDefinition
    public void initBeans(File file, String packageName, ClassLoader classLoader) {
        if(SysUtils.isWindows()){
            packageName = packageName.replace("/", "\\");
        }
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File file1 : files) {
                initBeans(file1, packageName, classLoader);
            }
        } else {

            // 获取文件路径
            String filePath = file.getAbsolutePath();
            filePath = filePath.substring(filePath.indexOf(packageName), filePath.indexOf(".class"));
            String classPath ="";
            if(SysUtils.isWindows()){
                classPath = filePath.replace("\\", ".");
            }else {
                classPath = filePath.replace("/",".");
            }
            try {
                // 加载类
                Class<?> aClass = classLoader.loadClass(classPath);
                if (aClass.isAnnotationPresent(Component.class)) {
                    // 是否有实现BeanPostProcessor
                    if (BeanPostProcessor.class.isAssignableFrom(aClass)) {
                        BeanPostProcessor beanPostProcessor = null;
                        try {
                            beanPostProcessor = (BeanPostProcessor) aClass.newInstance();
                        } catch (InstantiationException e) {
                            throw new RuntimeException(e);
                        } catch (IllegalAccessException e) {
                            throw new RuntimeException(e);
                        }
                        // 添加
                        beanPostProcessors.add(beanPostProcessor);
                    }
                    //定义BeanDefinition
                    Component annotation = aClass.getAnnotation(Component.class);
                    String beanName = annotation.value();
                    if(StrUtil.isBlank(beanName)){
                        beanName = aClass.getSimpleName().replace(aClass.getSimpleName().charAt(0), Character.toLowerCase(aClass.getSimpleName().charAt(0)));
                    }
                    BeanDefinition beanDefinition = new BeanDefinition();
                    if (aClass.isAnnotationPresent(Scope.class)) {
                        Scope scope = aClass.getAnnotation(Scope.class);
                        beanDefinition.setScope(scope.value());
                    } else {
                        beanDefinition.setScope("singleton");
                    }
                    beanDefinition.setBeanClass(aClass);
                    beanDefinitionMaps.put(beanName, beanDefinition);
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }


        }


    }

    public Object getBean(String beanName) {
        if (beanDefinitionMaps.containsKey(beanName)) {
            BeanDefinition beanDefinition = beanDefinitionMaps.get(beanName);
            if (beanDefinition.getScope().equals("singleton")) {
                // 从单例池中获取
                Object o = singletonObjects.get(beanName);
                return o;
            } else {
                // 创建对象
                return createBean(beanName,beanDefinition);

            }
        } else {
            throw new NullPointerException();
        }


    }

    public Object getBean(Class<?> clazz) {
        Set<Map.Entry<String, BeanDefinition>> entries = beanDefinitionMaps.entrySet();
        for (Map.Entry<String, BeanDefinition> entry : entries) {
            BeanDefinition beanDefinition = entry.getValue();
            String beanName = entry.getKey();
            if(beanDefinition.getBeanClass()==clazz){
                String key = entry.getKey();
                if(beanDefinition.getScope().equals("singleton")){
                    return singletonObjects.get(key);
                }else {
                    return createBean(beanName,beanDefinition);
                }

            }
        }
        return "";
    }

    public static void main(String[] args) {
        ApplicationContent applicationContent = new ApplicationContent(App.class);
        UserServiceImpl bean3 = (UserServiceImpl) applicationContent.getBean(UserServiceImpl.class);

        System.out.println(bean3.getOrderServiceImpl());
        System.out.println(bean3.getBeanName());


    }
}
